﻿#include "../console/console_impl.hh"

#include "../command/command.hh"

#include "../detail/box_argument_impl.hh"
#include "../detail/box_config_impl.hh"
#include "../detail/command_impl.hh"
#include "../detail/command_manager.hh"
#include "../detail/lang_impl.hh"
#include "../detail/proc_stat_impl.hh"
#include "../detail/scope_curl.hh"
#include "../detail/service_layer.hh"

#include "../config/box_config.hh"

#include "../util/os_util.hh"
#include "../util/string_util.hh"
#include "../util/time_system.hh"
#include "../util/timer_wheel.hh"

#include "../box/box_os.hh"
#include "../box/fixed_mem_pool.hh"
#include "../box/service_box.hh"
#include "../box/service_http_loader.hh"
#include "../box/version.hh"

#include "../repo/src/include/root/rpc_root.h"
#include "../repo/src/include/root/rpc_statistics.hh"

#include "include/json/json.h"
#include "kconfig/interface/config.h"

#include <fstream>
#include <functional>

namespace kratos {
namespace console {

/**
 * 控制台开关
 */
struct ConsoleSwitchImpl : public ConsoleSwitch {
  std::string display_name_; ///< 开关显示在页面上的名字
  std::string tips_;         ///< 鼠标悬停显示的提示
  bool on_off_;              ///< 状态，true表示开启，false表示关闭
  std::uint64_t user_data_{0}; ///< 用户数据
  virtual ~ConsoleSwitchImpl() {}
  virtual auto set_display_name(const std::string &display_name)
      -> void override {
    display_name_ = display_name;
  }
  virtual auto set_on_off(bool on_off) -> void override { on_off_ = on_off; }
  virtual auto set_tips(const std::string &tips) -> void override {
    tips_ = tips;
  }
  virtual auto get_user_data() -> std::uint64_t override { return user_data_; }
};

/**
 * 控制台选项
 */
struct ConsoleSelectionImpl : public ConsoleSelection {
  std::string display_name_;            ///< 开关显示在页面上的名字
  std::vector<std::string> selections_; ///< 选项列表，第一项为当前选项
  std::string tips_;                    ///< 鼠标悬停显示的提示
  std::uint64_t user_data_{0}; ///< 用户数据
  virtual ~ConsoleSelectionImpl() {}
  virtual auto set_display_name(const std::string &display_name)
      -> void override {
    display_name_ = display_name;
  }
  virtual auto add_selection(const std::string &selection) -> void override {
    selections_.emplace_back(selection);
  }
  virtual auto add_selection(const std::vector<std::string> &selections)
      -> void override {
    for (const auto &s : selections) {
      selections_.emplace_back(s);
    }
  }
  virtual auto set_tips(const std::string &tips) -> void override {
    tips_ = tips;
  }
  virtual auto get_user_data() -> std::uint64_t override {
    return user_data_;
  }
};

ConsoleImpl::ConsoleImpl(service::ServiceBox *box) {
  box_ = box;
  last_service_tick_ = std::time(nullptr);
}

ConsoleImpl::~ConsoleImpl() {}

auto ConsoleImpl::start() -> bool {
  if (!box_->get_config().has_attribute("command.enable")) {
    // 未开启
    return true;
  }
  if (box_->get_config().get_string("command.enable") != "true") {
    // 未开启
    return true;
  }
  if (!load_config()) {
    return false;
  }

  // 注册路径API
  register_api("/root", &ConsoleImpl::root_command);
  register_api("/basic", &ConsoleImpl::basic_command);
  register_api("/basic_memory", &ConsoleImpl::basic_memory);
  register_api("/detail_memory", &ConsoleImpl::detail_memory);
  register_api("/memory_history", &ConsoleImpl::memory_history);
  register_api("/registered_service", &ConsoleImpl::registered_service);
  register_api("/reference_service", &ConsoleImpl::reference_service);
  register_api("/loaded_service", &ConsoleImpl::loaded_service);
  register_api("/service_call", &ConsoleImpl::service_call);
  register_api("/detail_service_call", &ConsoleImpl::detail_service_call);
  register_api("/service_history", &ConsoleImpl::service_history);
  register_api("/all_switch", &ConsoleImpl::all_switch);
  register_api("/switch_on_off", &ConsoleImpl::switch_on_off);
  register_api("/all_selection", &ConsoleImpl::all_selection);
  register_api("/selection_change", &ConsoleImpl::selection_change);
  register_api("/current_version", &ConsoleImpl::current_version);
  register_api("/current_version_api", &ConsoleImpl::current_version_api);
  register_api("/install", &ConsoleImpl::install);

  // 注册开关API
  register_on_off_method("is_open_system_exception",
                         &ConsoleImpl::on_switch_is_open_system_exception);
  register_on_off_method("is_dump_heap", &ConsoleImpl::on_switch_is_dump_heap);
  register_on_off_method("is_open_remote_update",
                         &ConsoleImpl::on_switch_is_open_remote_update);
  register_on_off_method("is_open_rpc_stat",
                         &ConsoleImpl::on_switch_is_open_rpc_stat);
  register_on_off_method("is_gc_on_off", &ConsoleImpl::on_switch_is_gc_on_off);

  // 注册选项API
  register_selection_method("max_frame", &ConsoleImpl::on_select_max_frame);
  register_selection_method(
      "service_finder_connect_timeout",
      &ConsoleImpl::on_select_service_finder_connect_timeout);
  register_selection_method("connect_other_box_timeout",
                            &ConsoleImpl::on_select_connect_other_box_timeout);
  register_selection_method(
      "network_buffer_len",
      &ConsoleImpl::on_select_box_channel_recv_buffer_len);
  register_selection_method("http_max_call_timeout",
                            &ConsoleImpl::on_select_http_max_call_timeout);
  register_selection_method("remote_repo_check_interval",
                            &ConsoleImpl::on_select_remote_repo_check_interval);
  register_selection_method("gc_intval", &ConsoleImpl::on_select_gc_intval);
  return true;
}

auto ConsoleImpl::register_api(const std::string &path, APIClassMethod method)
    -> void {
  auto *command = new service::CommandImpl(box_->get_command_manager());
  command_map_[path].reset(command);
  command->wait_path(path, 5000,
                     std::bind(method, this, std::placeholders::_1));
}

auto ConsoleImpl::register_on_off_method(const std::string &switch_name,
                                         OnOffMethod method) -> void {
  on_off_method_map_[switch_name] =
      std::bind(method, this, std::placeholders::_1, std::placeholders::_2);
}

auto ConsoleImpl::register_selection_method(const std::string &attr_name,
                                            AttributeMethod method) -> void {
  attr_method_map_[attr_name] =
      std::bind(method, this, std::placeholders::_1, std::placeholders::_2,
                std::placeholders::_3);
}

auto ConsoleImpl::stop() -> bool { return true; }

auto ConsoleImpl::update() -> void {
  if (service_reload_count_ > 0) {
    // 手动更新版本
    service_reload_count_ -= box_->get_service_updater()->update_once();
  }
}

auto ConsoleImpl::add_switch(const std::string &name,
                             ConsoleSwitchPlugin switch_plugin) -> void {
  switch_plugin_map_[name] = switch_plugin;
}

auto ConsoleImpl::remove_switch(const std::string &name) -> void {
  switch_plugin_map_.erase(name);
}

auto ConsoleImpl::add_selection(const std::string &name,
                                ConsoleSelectionPlugin selection_plugin)
    -> void {
  selection_plugin_map_[name] = selection_plugin;
}

auto ConsoleImpl::remove_selection(const std::string &name) -> void {
  selection_plugin_map_.erase(name);
}

auto ConsoleImpl::get_current_user_data() -> std::uint64_t {
  return current_user_data_;
}

auto ConsoleImpl::load_config() -> bool {
  if (!box_->get_config().has_attribute("command.root_page_path")) {
    box_->write_log(lang::LangID::LANG_CONSOLE_CONFIG_ERROR,
                    klogger::Logger::FAILURE);
    return false;
  }
  root_path_html_file_path_ =
      box_->get_config().get_string("command.root_page_path");
  return true;
}

auto ConsoleImpl::root_command(const std::string &) -> std::string {
  if (!kratos::util::is_path_exists(root_path_html_file_path_)) {
    return "Page not found";
  }
  std::ifstream ifs;
  ifs.open(root_path_html_file_path_);
  if (!ifs) {
    return "Page not found";
  }
  std::string content;
  ifs.seekg(0, std::ios::end);
  content.reserve(ifs.tellg());
  ifs.seekg(0, std::ios::beg);
  content.assign((std::istreambuf_iterator<char>(ifs)),
                 std::istreambuf_iterator<char>());
  ifs.close();
  return content;
}

template <typename T>
auto make_basic(const char *name, const T &v, Json::Value &ret) -> void {
  Json::Value value;
  value["title"] = name;
  value["info"] = v;
  ret.append(value);
}

auto ConsoleImpl::basic_command(const std::string &) -> std::string {
  const auto &argument_impl =
      dynamic_cast<const argument::BoxArgumentImpl &>(box_->get_argument());
  const auto &config_impl =
      dynamic_cast<config::BoxConfigImpl &>(box_->get_config());

  Json::Value ret;

  make_basic("Version", get_version_string(), ret);
  make_basic("PID file", get_pid_file_path(box_), ret);

  make_basic("Configuration center API",
             argument_impl.get_config_center_api_url(), ret);
  make_basic("Start as proxy", argument_impl.is_proxy(), ret);
  make_basic("Proxy host", argument_impl.get_proxy_host(), ret);

  make_basic("Configuration file", config_impl.get_config_file_path(), ret);
  make_basic("Daemonized", config_impl.is_start_as_daemon(), ret);
  make_basic("Finder type", config_impl.get_service_finder_type(), ret);
  make_basic("Finder host", config_impl.get_service_finder_hosts(), ret);
  make_basic("Service finder connect timeout(millionsecond)",
             config_impl.get_service_finder_connect_timeout(), ret);
  make_basic("Service box connect timeout(millionsecond)",
             config_impl.get_connect_other_box_timeout(), ret);
  make_basic("Transport receiving buffer size(byte)",
             config_impl.get_box_channel_recv_buffer_len(), ret);
  make_basic("Coroutinized", config_impl.is_open_coroutine(), ret);
  make_basic("Online hotfix", config_impl.is_open_remote_update(), ret);
  make_basic("Online repo. version API",
             config_impl.get_remote_service_repo_version_api(), ret);
  make_basic("Online repo. latest version API",
             config_impl.get_remote_service_repo_latest_version_api(), ret);
  make_basic("Online repo. API", config_impl.get_remote_service_repo_dir(),
             ret);
  make_basic("Online repo. checking interval(second)",
             config_impl.get_remote_repo_check_interval(), ret);
  make_basic("HTTP request/response timeout(second)",
             config_impl.get_http_max_call_timeout(), ret);

  return ret.toStyledString();
}

auto ConsoleImpl::basic_memory(const std::string &) -> std::string {
  // 被使用的总内存
  Json::Value total_memory_in_use_value;
  total_memory_in_use_value["title"] = "Total memory in use";
  total_memory_in_use_value["info"] =
      util::readable_size(MempoolRef.mem_block_size_in_use());
  // 池内内存
  Json::Value total_memory_in_pool_value;
  total_memory_in_pool_value["title"] = "Total memory in pool";
  const auto &pools = MempoolRef.get_fixed_mem_pools();
  std::size_t size_in_pool = 0;
  for (const auto &pool : pools) {
    size_in_pool += pool.second->count_in_pool() * pool.second->fixed_size();
  }
  total_memory_in_pool_value["info"] = util::readable_size(size_in_pool);
  // 堆上内存
  Json::Value total_memory_in_heap;
  total_memory_in_heap["title"] = "Total memory in heap";
  total_memory_in_heap["info"] =
      util::readable_size(size_in_pool + MempoolRef.mem_block_size_in_use());
  // 进程堆内存
  Json::Value totol_proc_memory;
  totol_proc_memory["title"] = "Total memory in use of process";
  std::string proc_mem;
  box_->get_proc_stat()->get("mem_occupy(M)", proc_mem);
  totol_proc_memory["info"] = proc_mem;
  // 限制记录数量
  if (memory_history_.size() > 1000) {
    memory_history_.pop_front();
  }
  Json::Value total_timer_count;
  total_timer_count["title"] = "Total timer count";
  total_timer_count["info"] = kratos::util::get_wheel_timer_count();
  // 记录历史
  memory_history_.push_back(
      {time_system::Datetime(std::time(nullptr)).to_string(),
       size_in_pool + MempoolRef.mem_block_size_in_use()});
  // 返回
  Json::Value ret;
  ret.append(total_memory_in_use_value);
  ret.append(total_memory_in_pool_value);
  ret.append(total_memory_in_heap);
  ret.append(totol_proc_memory);
  ret.append(total_timer_count);
  return ret.toStyledString();
}

auto ConsoleImpl::detail_memory(const std::string &) -> std::string {
  Json::Value ret;
  const auto &fixed_pools = MempoolRef.get_fixed_mem_pools();
  for (const auto &[_, pool] : fixed_pools) {
    auto fix_size = pool->fixed_size();
    auto count_in_pool = pool->count_in_pool();
    auto count_in_use = pool->count_in_use();
    Json::Value value;
    value["block_size"] = fix_size;
    value["in_pool_size"] = util::readable_size(fix_size * count_in_pool);
    value["in_use_size"] = util::readable_size(fix_size * count_in_use);
    value["heap_size"] =
        util::readable_size(fix_size * (count_in_pool + count_in_use));
    ret.append(value);
  }
  return ret.toStyledString();
}

auto ConsoleImpl::memory_history(const std::string &) -> std::string {
  Json::Value ret;
  for (const auto &spot : memory_history_) {
    Json::Value value;
    value["date"] = spot.date;
    value["units"] = spot.size;
    ret.append(value);
  }
  return ret.toStyledString();
}

auto ConsoleImpl::registered_service(const std::string &) -> std::string {
  Json::Value ret;
  for (const auto &[k, v] : box_->get_register_services()) {
    Json::Value value;
    if (util::startWith(k, "/")) {
      value["title"] = k;
    } else {
      value["title"] = "/" + k;
    }
    value["info"] = v;
    ret.append(value);
  }
  return ret.toStyledString();
}

auto ConsoleImpl::reference_service(const std::string &) -> std::string {
  Json::Value ret;
  for (const auto &[k, v] : box_->get_service_layer()->get_remote_service()) {
    Json::Value value;
    value["title"] = k;
    value["info"] = v.host_vec.size();
    ret.append(value);
  }
  return ret.toStyledString();
}

auto ConsoleImpl::loaded_service(const std::string &) -> std::string {
  Json::Value ret;
  for (const auto &[k, v] : box_->get_config().get_preload_service()) {
    Json::Value value;
    value["title"] = k;
    value["info"] = v;
    ret.append(value);
  }
  return ret.toStyledString();
}

auto ConsoleImpl::service_call(const std::string &) -> std::string {
  Json::Value ret;
  const auto &pre_load_service = box_->get_config().get_preload_service();
  auto *stat = rpc::getStatistic();
  std::size_t total_call_count = 0;
  for (const auto &[uuid, service] : *stat->getServiceStatistics()) {
    Json::Value value;
    value["service_name"] = pre_load_service.at(std::to_string(uuid));
    value["total_call_count"] = service->getCallCount();
    value["total_success_call_count"] = service->getCallSuccessCount();
    value["QPS"] = service->getQPS();
    value["PeekQPS"] = service->getPeakQPS();
    value["MinCost"] = service->getMinCost();
    value["MaxCost"] = service->getMaxCost();
    value["AvgCost"] = service->getAvgCost();
    ret.append(value);
    total_call_count += service->getCallCount();
  }
  auto now = std::time(nullptr);
  // 限制记录数量
  if (service_history_.size() > 1000) {
    service_history_.pop_front();
  }
  // 记录历史
  if (!last_service_tick_) {
    service_history_.push_back(
        {time_system::Datetime(std::time(nullptr)).to_string(),
         total_call_count});
  } else {
    auto gap = (now - last_service_tick_);
    if (gap == 0) {
      gap = 1;
    }
    service_history_.push_back(
        {time_system::Datetime(std::time(nullptr)).to_string(),
         (total_call_count - last_total_call_count_) / gap});
  }
  last_total_call_count_ = total_call_count;
  last_service_tick_ = now;
  return ret.toStyledString();
}

auto ConsoleImpl::detail_service_call(const std::string &) -> std::string {
  Json::Value ret;
  const auto &pre_load_service = box_->get_config().get_preload_service();
  auto *stat = rpc::getStatistic();
  for (const auto &[uuid, service] : *stat->getServiceStatistics()) {
    const auto &service_name = pre_load_service.at(std::to_string(uuid));
    for (const auto &[method_id, method] : service->getMethodStatistics()) {
      Json::Value value;
      value["service_name"] = service_name;
      value["method_name"] = method->getMethodFullName();
      value["total_call_count"] = method->getCallCount();
      value["total_success_call_count"] = method->getCallSuccessCount();
      value["QPS"] = method->getQPS();
      value["PeekQPS"] = method->getPeakQPS();
      value["MinCost"] = method->getMinCost();
      value["MaxCost"] = method->getMaxCost();
      value["AvgCost"] = method->getAvgCost();
      ret.append(value);
    }
  }
  return ret.toStyledString();
}

auto ConsoleImpl::service_history(const std::string &) -> std::string {
  Json::Value ret;
  for (const auto &spot : service_history_) {
    Json::Value value;
    value["date"] = spot.date;
    value["units"] = spot.size;
    ret.append(value);
  }
  return ret.toStyledString();
}

auto ConsoleImpl::all_switch(const std::string &) -> std::string {
  Json::Value ret;
  const auto &argument_impl =
      dynamic_cast<const argument::BoxArgumentImpl &>(box_->get_argument());

  add_switch(
      ret, "is_open_system_exception", "Open std::exception",
      argument_impl.is_open_system_exception(),
      "Setting on means takeover OS trap signal and throw std::exception");

  add_switch(ret, "is_dump_heap", "Dump allocation info.",
             argument_impl.is_dump_heap(),
             "Setting on means dump user service's memory allocator");

  add_switch(ret, "is_open_remote_update", "Remote update",
             box_->get_config().is_open_remote_update(),
             "Setting on means check and update patch automatically");

  add_switch(ret, "is_open_rpc_stat", "RPC statistics",
             box_->get_config().is_open_rpc_stat(),
             "Turns RPC statistics on or off");

  add_switch(ret, "is_gc_on_off", "Memory pool GC", MempoolRef.get_gc_on_off(),
             "Turns GC on or off");

  // 添加用户开关
  for (const auto &[k, plugin] : switch_plugin_map_) {
    ConsoleSwitchImpl cs;
    cs.user_data_ = plugin.user_data;
    plugin.refresh_method(*this, cs);
    add_switch(ret, k, cs.display_name_, cs.on_off_, cs.tips_);
  }

  return ret.toStyledString();
}

auto ConsoleImpl::add_switch(Json::Value &ret, const std::string &switch_name,
                             const std::string &display_name, bool on_off,
                             const std::string &tips) -> void {
  Json::Value value;
  value["title"] = switch_name;
  value["display_name"] = display_name;
  value["on_off"] = on_off;
  value["tips"] = tips;
  ret.append(value);
}

auto ConsoleImpl::add_selection(Json::Value &ret, const std::string &attr_name,
                                const std::string &display_name,
                                const std::vector<std::string> &selections,
                                const std::string &tips) -> void {
  Json::Value value;
  value["attr_name"] = attr_name;
  value["display_name"] = display_name;
  value["tips"] = tips;
  Json::Value selection_values;
  for (const auto &v : selections) {
    Json::Value temp_value;
    temp_value["value"] = v;
    selection_values.append(temp_value);
  }
  value["selection"] = selection_values;
  ret.append(value);
}

auto ConsoleImpl::parse_switch_on_off_json(const std::string &switch_json,
                                           Json::Value &root) -> bool {
  Json::CharReaderBuilder builder;
  std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
  const auto *start = switch_json.c_str();
  const auto *end = switch_json.c_str() + switch_json.size();
  Json::String error;
  if (!reader->parse(start, end, &root, &error)) {
    return false;
  }
  if (!root.isMember("title")) {
    return false;
  }
  if (!root.isMember("on_off")) {
    return false;
  }
  return true;
}

auto ConsoleImpl::switch_on_off(const std::string &switch_json) -> std::string {
  Json::Value ret;
  Json::Value root;
  if (!parse_switch_on_off_json(switch_json, root)) {
    ret["error"] = "fail";
    ret["error_str"] = "Invalid json format";
    return ret.toStyledString();
  }
  auto switch_name = root["title"].asString();
  auto on_off = root["on_off"].asBool();
  std::string result;
  auto it = on_off_method_map_.find(switch_name);
  if (it == on_off_method_map_.end()) {
    if (!try_call_plugin_switch(switch_name, on_off, result)) {
      ret["error"] = "fail";
      if (result.empty()) {
        ret["error_str"] = "Switch not found";
      } else {
        ret["error_str"] = result;
      }
    } else {
      ret["error"] = "ok";
    }
  } else {
    if (!it->second(on_off, result)) {
      ret["error"] = "fail";
      ret["error_str"] = result;
    } else {
      ret["error"] = "ok";
    }
  }
  return ret.toStyledString();
}

auto ConsoleImpl::on_switch_is_open_system_exception(bool, std::string &error)
    -> bool {
  std::string command = "is_open_system_exception ";
  bool on_off = !box_->get_argument().is_open_system_exception();
  if (on_off) {
    command += " true";
  } else {
    command += " false";
  }
  return dynamic_cast<const argument::BoxArgumentImpl &>(box_->get_argument())
      .on_change(command, error);
}

auto ConsoleImpl::on_switch_is_dump_heap(bool, std::string &error) -> bool {
  std::string command = "is_dump_heap ";
  const auto &impl =
      dynamic_cast<const argument::BoxArgumentImpl &>(box_->get_argument());
  bool on_off = !impl.is_dump_heap();
  if (on_off) {
    command += " true";
  } else {
    command += " false";
  }
  return impl.on_change(command, error);
}

auto ConsoleImpl::on_switch_is_open_remote_update(bool on_off, std::string &)
    -> bool {
  auto &impl = dynamic_cast<config::BoxConfigImpl &>(box_->get_config());
  impl.set_is_open_remote_update(!impl.is_open_remote_update());
  return true;
}

auto ConsoleImpl::on_switch_is_open_rpc_stat(bool, std::string &) -> bool {
  auto &impl = dynamic_cast<config::BoxConfigImpl &>(box_->get_config());
  auto on_off = !impl.is_open_rpc_stat();
  impl.set_is_open_rpc_stat(on_off);
  box_->get_rpc()->switchRpcStat(on_off);
  return true;
}

auto ConsoleImpl::on_switch_is_gc_on_off(bool, std::string &) -> bool {
  auto on_off = !MempoolRef.get_gc_on_off();
  MempoolRef.turn_on_off_gc(on_off);
  return true;
}

auto ConsoleImpl::try_call_plugin_switch(const std::string &name, bool on_off,
                                         std::string &result) -> bool {
  auto it = switch_plugin_map_.find(name);
  if (it == switch_plugin_map_.end()) {
    return false;
  }
  current_user_data_ = it->second.user_data;
  return it->second.change_method(*this, on_off, result);
}

auto ConsoleImpl::all_selection(const std::string &) -> std::string {
  Json::Value ret;

  // Maximum frame per second
  add_selection(
      ret, "max_frame", "Maximum frame per second",
      {
          std::to_string(box_->get_argument().get_max_frame()), // 当前值
          "15", "20", "25", "30", "40", "50", "100"             // 可选值
      },
      "Maximum frame per second");

  // Service finder connect timeout
  add_selection(
      ret, "service_finder_connect_timeout",
      "Service finder connect timeout(millionsecond)",
      {
          std::to_string(box_->get_config()
                             .get_service_finder_connect_timeout()), // 当前值
          "1000", "2000", "3000", "4000", "5000", "6000", "7000", "8000",
          "9000", "10000" // 可选值
      },
      "Service finder connect timeout(millionsecond)");

  // The timeout of connecting other service box
  add_selection(
      ret, "The timeout of connecting other service box",
      "The timeout of connecting other service box(millionsecond)",
      {
          std::to_string(
              box_->get_config().get_connect_other_box_timeout()), // 当前值
          "1000", "2000", "3000", "4000", "5000", "6000", "7000", "8000",
          "9000", "10000" // 可选值
      },
      "The timeout of connecting other service box(millionsecond)");

  // Network buffer length(recv/send)
  add_selection(
      ret, "Network buffer length", "Network buffer length",
      {
          util::readable_size(
              box_->get_config().get_box_channel_recv_buffer_len()), // 当前值
          "8K", "16K", "32K", "64K", "256K", "512K", "1M", "2M", "4M",
          "8M" // 可选值
      },
      "Network buffer length");

  // HTTP调用/请求超时
  add_selection(
      ret, "HTTP timeout", "HTTP timeout(second)",
      {
          std::to_string(
              box_->get_config().get_http_max_call_timeout()), // 当前值
          "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"    // 可选值
      },
      "HTTP timeout(second)");

  // 远程更新检查周期
  add_selection(
      ret, "Remote repo checking interval",
      "Remote repo checking interval(second)",
      {
          std::to_string(
              box_->get_config().get_remote_repo_check_interval()), // 当前值
          "10", "20", "30", "40", "50", "60", "120", "180", "240",
          "300" // 可选值
      },
      "Remote repo checking interval(second)");

  // GC间隔，毫秒
  add_selection(ret, "GC interval", "GC interval(millionsecond)",
                {
                    std::to_string(MempoolRef.get_gc_intval()), // 当前值
                    "100", "200", "300", "400", "500", "600", "700", "800",
                    "900", "1000" // 可选值
                },
                "GC interval(millionsecond)");

  // 用户的选项
  for (const auto &[k, plugin] : selection_plugin_map_) {
    ConsoleSelectionImpl cs;
    cs.user_data_ = plugin.user_data;
    plugin.refresh_method(*this, cs);
    add_selection(ret, k, cs.display_name_, cs.selections_, cs.tips_);
  }
  return ret.toStyledString();
}

auto ConsoleImpl::selection_change(const std::string &new_attr) -> std::string {
  Json::Value ret;
  // 解析JSON
  Json::Value root;
  std::string result;
  ret["error"] = "ok";
  if (!parse_attr_json(new_attr, root)) {
    ret["error"] = "fail";
    ret["error_str"] = "Invalid json format";
  } else {
    auto attr_name = root["attr_name"].asString();
    auto it = attr_method_map_.find(attr_name);
    if (it == attr_method_map_.end()) {
      if (!try_call_selection_plugin(attr_name, root["value"].asString(),
                                     result)) {
        if (result.empty()) {
          ret["error"] = "fail";
          ret["error_str"] = "Attribute not found";
        } else {
          ret["error"] = "fail";
          ret["error_str"] = result;
        }
      } else {
        ret["error"] = "ok";
      }
    } else {
      if (!it->second(root["attr_name"].asString(), root["value"].asString(),
                      result)) {
        ret["error"] = "fail";
        ret["error_str"] = result;
      } else {
        ret["error"] = "ok";
      }
    }
  }
  return ret.toStyledString();
}

auto ConsoleImpl::current_version(const std::string &) -> std::string {
  Json::Value ret;
  const auto &version_api =
      box_->get_config().get_remote_service_repo_version_api();
  if (box_->get_config().is_open_remote_update()) {
    // 自动更新开启的情况下，外部不能干预热更新
    ret["error"] = "Auto-update is ON";
  } else if (version_api.empty()) {
    ret["error"] = "No version API setting";
  } else {
    ret["error"] = "ok";
  }
  return ret.toStyledString();
}

auto ConsoleImpl::current_version_api(const std::string &) -> std::string {
  std::string result;
  Json::Value ret;
  kratos::service::ScopeCurl curl;
  if (curl.perform(box_->get_config().get_remote_service_repo_version_api())) {
    curl.to_string(result);
    std::string error;
    if (!parse_json(ret, result, error)) {
      ret["error"] = "Invalid JSON format";
      return ret.toStyledString();
    }
    if (!ret.isMember("version")) {
      ret["error"] = "Invalid JSON format";
      return ret.toStyledString();
    }
    std::string version = ret["version"].asString();
    ret["error"] = "ok";
    if (service::ServiceHttpLoader::check_version_exists(box_, version)) {
      // 版本已经安装过
      ret["disable"] = true;
    } else {
      ret["disable"] = false;
    }
  } else {
    ret["error"] = "fail";
  }
  return ret.toStyledString();
}

auto ConsoleImpl::parse_json(Json::Value &root, const std::string &json_string,
                             std::string &error) -> bool {
  Json::CharReaderBuilder builder;
  std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
  const auto *start = json_string.c_str();
  const auto *end = json_string.c_str() + json_string.size();
  if (!reader->parse(start, end, &root, &error)) {
    return false;
  }
  return true;
}

auto ConsoleImpl::parse_json_and_get_attribute(const std::string &json_string,
                                               const std::string &name,
                                               std::string &value) -> bool {
  Json::CharReaderBuilder builder;
  std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
  const auto *start = json_string.c_str();
  const auto *end = json_string.c_str() + json_string.size();
  std::string error;
  Json::Value root;
  if (!reader->parse(start, end, &root, &error)) {
    return false;
  }
  if (!root.isMember(name)) {
    return false;
  }
  value = root[name].asString();
  return true;
}

auto ConsoleImpl::install(const std::string &json_str) -> std::string {
  Json::Value ret;
  if (service_reload_count_ > 0) {
    ret["error"] = "In progress";
    return ret.toStyledString();
  } else {
    auto version_json = current_version_api("");
    std::string version;
    if (!parse_json_and_get_attribute(json_str, "version", version)) {
      ret["error"] = "Invalid version file";
      return ret.toStyledString();
    }
    if (service::ServiceHttpLoader::check_version_exists(box_, version)) {
      ret["error"] = "Already installed";
      return ret.toStyledString();
    }
    Json::String error;
    Json::Value root;
    if (!parse_json(root, version_json, error)) {
      ret["error"] = "Invalid version file";
      return ret.toStyledString();
    } else {
      if (!root.isMember("version") || !root.isMember("bundles")) {
        ret["error"] = "Invalid version file";
        return ret.toStyledString();
      }
      if (version != root["version"].asString()) {
        ret["error"] = "Version mismatch";
        return ret.toStyledString();
      }
      // 检查版本是否匹配，需要更新的bundle必须是已经被加载的
      const auto &registered_services = box_->get_register_services();
      for (const auto &bundle : root["bundles"]) {
        if (!box_->is_installed(bundle["uuid"].asString())) {
          ret["error"] = "Mismatched patch";
          return ret.toStyledString();
        }
      }
      service_reload_count_ = root["bundles"].size();
    }
  }
  box_->get_service_updater()->force_update();
  ret["error"] = "ok";
  return ret.toStyledString();
}

auto ConsoleImpl::parse_attr_json(const std::string &new_attr_json,
                                  Json::Value &root) -> bool {
  Json::CharReaderBuilder builder;
  std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
  const auto *start = new_attr_json.c_str();
  const auto *end = new_attr_json.c_str() + new_attr_json.size();
  Json::String error;
  if (!reader->parse(start, end, &root, &error)) {
    return false;
  }
  if (!root.isMember("attr_name")) {
    return false;
  }
  if (!root.isMember("value")) {
    return false;
  }
  return true;
}

auto ConsoleImpl::on_select_max_frame(const std::string &,
                                      const std::string &value, std::string &)
    -> bool {
  dynamic_cast<const argument::BoxArgumentImpl &>(box_->get_argument())
      .set_max_frame(std::stoi(value));
  return true;
}

auto ConsoleImpl::on_select_service_finder_connect_timeout(
    const std::string &, const std::string &value, std::string &) -> bool {
  dynamic_cast<config::BoxConfigImpl &>(box_->get_config())
      .set_service_finder_connect_timeout(std::stoi(value));
  return true;
}

auto ConsoleImpl::on_select_connect_other_box_timeout(const std::string &,
                                                      const std::string &value,
                                                      std::string &) -> bool {
  dynamic_cast<config::BoxConfigImpl &>(box_->get_config())
      .set_connect_other_box_timeout(std::stoi(value));
  return true;
}

auto ConsoleImpl::on_select_box_channel_recv_buffer_len(
    const std::string &, const std::string &value, std::string &) -> bool {
  dynamic_cast<config::BoxConfigImpl &>(box_->get_config())
      .set_box_channel_recv_buffer_len(
          (int)util::from_readable_size_string(value));
  return true;
}

auto ConsoleImpl::on_select_http_max_call_timeout(const std::string &,
                                                  const std::string &value,
                                                  std::string &) -> bool {
  dynamic_cast<config::BoxConfigImpl &>(box_->get_config())
      .set_http_max_call_timeout(std::stoi(value));
  return true;
}

auto ConsoleImpl::on_select_remote_repo_check_interval(const std::string &,
                                                       const std::string &value,
                                                       std::string &) -> bool {
  dynamic_cast<config::BoxConfigImpl &>(box_->get_config())
      .set_remote_repo_check_interval(std::stoi(value));
  return true;
}

auto ConsoleImpl::on_select_gc_intval(const std::string &,
                                      const std::string &value, std::string &)
    -> bool {
  MempoolRef.set_gc_intval(std::stoull(value));
  return true;
}

auto ConsoleImpl::try_call_selection_plugin(const std::string &name,
                                            const std::string &value,
                                            std::string &result) -> bool {
  auto it = selection_plugin_map_.find(name);
  if (it == selection_plugin_map_.end()) {
    return false;
  }
  current_user_data_ = it->second.user_data;
  return it->second.select_method(*this, value, result);
}

} // namespace console
} // namespace kratos
